package com.gaojinqi.base.core.jpa.domain;

import com.gaojinqi.base.core.jpa.enums.ConditionOperatorEnum;
import com.gaojinqi.base.core.jpa.enums.PropertyOperatorEnum;
import lombok.AllArgsConstructor;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * 自定义jpa通用基础查询条件封装类 <BR>
 * 用法：<BR>
 * 1、等值+AND查询，直接传入数据库实体entity即可
 * 2、混合查询，可通过{@link SimpleJpaQuery#addNormalCondition(PropertyOperatorEnum, String, Object)}等方法添加条件条件，用于执行查询
 *
 * @author gaojinqi
 * @version 1.0
 * @since 2020年04月10日
 */
public class SimpleJpaQuery<T> {

    /**
     * 查询实体（默认只取出非空条件进行等值条件搜索）
     */
    private final T entity;

    /**
     * 条件
     */
    private final List<Condition> conditions;

    /**
     * 是否开启where条件过滤
     */
    private boolean whereFilter = false;

    /**
     * 前提：开启{@link SimpleJpaQuery#whereFilter} ，需进行条件判断的字段（包括空值）
     */
    private Set<String> whereSelectiveKey = null;

    /**
     * 前提：开启{@link SimpleJpaQuery#whereFilter} ，无需进行条件判断的字段
     */
    private Set<String> whereExceptKey = null;

    /**
     * 等值+AND查询
     *
     * @param entity
     */
    public SimpleJpaQuery(T entity) {
        super();
        this.entity = entity;
        this.conditions = null;
    }

    /**
     * 自定义查询
     */
    public SimpleJpaQuery() {
        super();
        this.entity = null;
        this.conditions = new LinkedList<>();
    }

    /**
     * 内部类，用于填充非等值/非AND条件 <BR>
     */
    @AllArgsConstructor
    public static class Condition {
        /**
         * 该条件中, 属性和取值的关系运算符. 默认为相等, 即"=".
         */
        PropertyOperatorEnum propertyOperator;

        /**
         * 参数名
         */
        String property;

        /**
         * 参数值1
         */
        Object value1;

        /**
         * 参数值2
         */
        Object value2;
        /**
         * AND OR 逻辑运算符
         */
        ConditionOperatorEnum conditionOperator;

    }

    /**
     * 添加查询条件（默认AND查询条件）
     *
     * @param propertyOperator 属性和取值的关系运算符
     * @param property         参数名
     * @param value1           参数值1
     */
    public void addNormalCondition(PropertyOperatorEnum propertyOperator, String property, Object value1) {
        conditions.add(new Condition(propertyOperator, property, value1, null, ConditionOperatorEnum.AND));
    }

    /**
     * 添加查询条件
     *
     * @param propertyOperator  属性和取值的关系运算符
     * @param property          参数名
     * @param value1            参数值1
     * @param conditionOperator AND OR 逻辑运算符
     */
    public void addNormalCondition(PropertyOperatorEnum propertyOperator, String property, Object value1,
                                   ConditionOperatorEnum conditionOperator) {
        conditions.add(new Condition(propertyOperator, property, value1, null, conditionOperator));
    }

    /**
     * 添加查询条件，通用。
     *
     * @param propertyOperator  属性和取值的关系运算符
     * @param property          参数名
     * @param value1            参数值1
     * @param value2            参数值2
     * @param conditionOperator AND OR 逻辑运算符
     */
    public void addNormalCondition(PropertyOperatorEnum propertyOperator, String property, Object value1, Object value2,
                                   ConditionOperatorEnum conditionOperator) {
        conditions.add(new Condition(propertyOperator, property, value1, value2, conditionOperator));
    }

    /**
     * 添加查询条件（默认等值+AND查询条件）
     *
     * @param property 参数名
     * @param value1   参数值1
     */
    public void addEqualCondition(String property, Object value1) {
        conditions.add(new Condition(PropertyOperatorEnum.EQUAL, property, value1, null, ConditionOperatorEnum.AND));
    }

    /**
     * 添加{@link PropertyOperatorEnum#BETWEEN}查询条件（AND查询条件）
     *
     * @param property 参数名
     * @param value1   参数值1
     * @param value2   参数值2
     */
    public void addBetweenCondition(String property, Object value1, Object value2) {
        if (value1 != null && value2 != null) {
            conditions.add(
                    new Condition(PropertyOperatorEnum.BETWEEN, property, value1, value2, ConditionOperatorEnum.AND));
        }
    }

    /**
     * 添加{@link PropertyOperatorEnum#BETWEEN}查询条件
     *
     * @param property          参数名
     * @param value1            参数值1
     * @param value2            参数值2
     * @param conditionOperator AND OR 逻辑运算符
     */
    public void addBetweenCondition(String property, Object value1, Object value2,
                                    ConditionOperatorEnum conditionOperator) {
        if (value1 != null && value2 != null) {
            conditions.add(new Condition(PropertyOperatorEnum.BETWEEN, property, value1, value2, conditionOperator));
        }
    }

    /**
     * 开启自定义查询条件过滤
     */
    public SimpleJpaQuery<T> openWhereFilter() {
        this.whereFilter = true;
        if (null == whereSelectiveKey) {
            synchronized (this) {
                if (null == whereSelectiveKey) {
                    whereSelectiveKey = new HashSet<>();
                }

            }
        }
        if (null == whereExceptKey) {
            synchronized (this) {
                if (null == whereExceptKey) {
                    whereExceptKey = new HashSet<>();
                }

            }
        }
        return this;
    }

    /**
     * 新增需进行条件判断的字段
     */
    public SimpleJpaQuery<T> addSelectiveKey(String... keys) {
        for (String key : keys) {
            whereSelectiveKey.add(key);
        }
        return this;
    }

    /**
     * 新增无需进行条件判断的字段
     */
    public SimpleJpaQuery<T> addExceptKey(String... keys) {
        for (String key : keys) {
            whereExceptKey.add(key);
        }
        return this;
    }

    /**
     * 查询是否存在需进行条件判断的字段
     */
    public boolean containsSelectiveKey(String key) {
        if (whereFilter && whereSelectiveKey.size() > 0) {
            return whereSelectiveKey.contains(key);
        }
        return false;
    }

    /**
     * 查询是否存在无需进行条件判断的字段
     */
    public boolean containsExceptKey(String key) {
        if (whereFilter && whereExceptKey.size() > 0) {
            return whereExceptKey.contains(key);
        }
        return false;
    }

    /**
     * Returns the {@link #entity}.
     *
     * @return the entity.
     */
    public Object getEntity() {
        return entity;
    }

    /**
     * Returns the {@link #whereFilter}.
     *
     * @return the whereFilter.
     */
    public boolean isWhereFilter() {
        return whereFilter;
    }

    /**
     * Returns the {@link #conditions}.
     *
     * @return the conditions.
     */
    public List<Condition> getConditions() {
        return conditions;
    }
}
